package client

import (
	"flag"
	"fmt"
	"os"
	"publicChainSystemTest/block"
	"publicChainSystemTest/tools"
	"publicChainSystemTest/transaction"
)

/**
 *@author:zhengyilong
 *@email:1797344574@qq.com
 *@phone:13479394730
 *@DateTime:2022/4/17 20:18
 **/

type Cli struct {

}

func (cl *Cli) Run() {
	switch os.Args[1] {
	case "createchain":
		cl.createchain()
	//case "addblock":
	//	cl.addblock()
	case "send":
		cl.send()
	case "printblock":
		cl.printblock()
	case "getblockcount":
		cl.getblockcount()
	case "help":
		cl.help()
	}
}

//包内方法，只可在本包内通过cl对象调用，配合Run()方法中的switch case使用。方便Run()方法简洁明了
func (cl *Cli)createchain() {
	//设置功能名并获取功能对象
	createchain := flag.NewFlagSet("createchain", flag.ExitOnError)

	//设置参数
	address := createchain.String("address", "", "账户名称")
	createchain.Parse(os.Args[2:])

	//判断区块是否已存在
	exist := tools.FileExist("./chain.db")
	if exist {
		fmt.Println("已经存在")
		return
	}

	//创建区块
	bc, err := block.NewBlockChain(*address)
	defer bc.Bolt.Close()
	if err != nil {
		fmt.Println(err.Error())
		return
	}

	fmt.Println("创建区块链成功")
}

//包内方法，只可在本包内通过cl对象调用，配合Run()方法中的switch case使用。方便Run()方法简洁明了
//func (cl *Cli)addblock() {
//	//设置功能名并获取功能对象
//	addblock := flag.NewFlagSet("addblock", flag.ExitOnError)
//
//	//设置参数
//	s := addblock.String("data", "", "添加区块的交易信息")
//	addblock.Parse(os.Args[2:])
//
//	//判断区块链是否存在
//	exist := tools.FileExist("./chain.db")
//	if !exist {
//		fmt.Println("无区块链")
//		return
//	}
//
//	//获取blockChain对象
//	chain, err := block.NewBlockChain(nil)
//	defer chain.Bolt.Close()
//	if err != nil {
//		fmt.Println(err.Error())
//		return
//	}
//
//	//添加区块
//	err = chain.Addblock([]byte(*s))
//	if err != nil {
//		fmt.Println(err.Error())
//		return
//	}
//
//	fmt.Println("添加成功")
//}

//发起一笔交易，把区块再添加到区块链里面
func (cl *Cli) send() {
	send := flag.NewFlagSet("send", flag.ExitOnError)
	from := send.String("from", "", "交易发起者的地址")
	to := send.String("to", "", "交易接受者的地址")
	amout := send.Uint("amout", 0, "交易金额")

	err := send.Parse(os.Args[2:])
	if err != nil {
		fmt.Println("解析失败",err.Error())
		return
	}

	tx, err := transaction.NewTransaction(*from, *to, *amout)
	if err != nil {
		fmt.Println(err.Error())
		return
	}

	bc, err := block.NewBlockChain("")
	defer bc.Bolt.Close()
	if err != nil {
		fmt.Println(err.Error())
		return
	}

	cb, err := transaction.NewCoinbase(*from)
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	err = bc.Addblock([]transaction.Transaction{*tx, *cb})
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	fmt.Println("添加成功")
}

//包内方法，只可在本包内通过cl对象调用，配合Run()方法中的switch case使用。方便Run()方法简洁明了
func (cl *Cli)printblock() {
	//判断区块链是否存在
	exist := tools.FileExist("./chain.db")
	if !exist {
		fmt.Println("无区块链")
		return
	}

	//获取blockChain对象
	bc, err := block.NewBlockChain(nil)
	defer bc.Bolt.Close()
	if err != nil {
		fmt.Println(err.Error())
		return
	}

	//输出区块
	allBlock, err := bc.GetAllBlock()
	if err != nil {
		fmt.Println("获取失败：",err)
		return
	}
	for _,block := range allBlock {
		fmt.Println()
		fmt.Printf("prvehash：%x\n",block.PrevHash)
		fmt.Printf("time：%d\n",block.Timestamp)
		fmt.Printf("hash：%x\n",block.Hash)
		fmt.Printf("data：%s\n",block.Data)
		fmt.Printf("nonce：%d\n",block.Nonce)
	}
}

//包内方法，只可在本包内通过cl对象调用，配合Run()方法中的switch case使用。方便Run()方法简洁明了
func (cl *Cli)getblockcount() {
	//判断区块链是否存在
	exist := tools.FileExist("./chain.db")
	if !exist {
		fmt.Println("无区块链")
		return
	}

	//获取blockChain对象
	bc, err := block.NewBlockChain(nil)
	defer bc.Bolt.Close()
	if err != nil {
		fmt.Println(err.Error())
		return
	}

	//获取个数
	allBlock, err := bc.GetAllBlock()
	if err != nil {
		fmt.Println("获取失败：",err)
		return
	}

	fmt.Printf("区块链含有%d个区块\n",len(allBlock))
}

//包内方法，只可在本包内通过cl对象调用，配合Run()方法中的switch case使用。方便Run()方法简洁明了
func (cl *Cli)help() {
	fmt.Println("\ta.创建带有创世区块的区块链，参数：有 1个 创世区块的交易信息\n\tmain.exe createchain --address \"123\"\n\t" +
		"b.发起一笔交易，参数：有 3个\n\tmain.exe send --from \"zhangsan\" --to \"liu\" --amout 50\n\t" +
		"c.打印所有区块信息\t，参数： 无\n\tmain.exe printblock\n\t" +
		"d.获取当前区块链中区块的个数，参数：无\n\tmain.exe getblockcount\n\t" +
		"e.输出当前系统的使用说明，参数：无\n\tmain.exe help")
}